/******************************************************************************
 * @file     drv_qspi.c
 * @brief    dwc_ssi driver for nor-flash target.
 * @version  V1.00
 * @date     2024-12-12
 ******************************************************************************/


#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "evalsoc.h"
#include "dbg_cli.h"





int qspi_read_status(dw_qspi_regs_t *pqspi, uint8_t *pstus, uint8_t cmd); // 添加这行声明



/*
@brief
	通过读取 mtime, mtime_h, 获取 64 位 tick 值.
	用来判断某些预期动作, 是否按照预期响应.
	用于简单系统, 不适用 CLINT 中断模式.

*/
bool time_reached( uint64_t tmo )
{
	uint64_t temp;

	temp = __get_rv_time();
	return (bool)(temp >= tmo);
}


/*
@brief
	产生一个预取超时时间.
*/
uint64_t time_make_timeout( uint32_t ticks )
{
	uint64_t temp;
	temp = __get_rv_time();
	return temp + ticks;
}



static void temp_delay( uint32_t ticks )
{
	for ( uint32_t i=0; i<ticks; i++ )  {
		asm volatile ( "nop" );
	}
}


//disable xip
void qspi_xip_0(){

	dw_qspi_regs_t *pqspi = (dw_qspi_regs_t *)0x2000000;

	/*xip_en = 0;*/
	REG32( 0x200000 + 0x10 ) = 0x4;

	// ssi enable...XIP_WRAP_INST 
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->CTRLR0 = 0x800807;// Quad SPI Format
	pqspi->SPI_CTRLR0 = 0x8100218;


}



/// @brief 发送cmd命令到qspi总线.
/// @param pqspi 指向qspi寄存器的指针.
/// @param cmd 要发送的命令.
/// @return 0表示成功, 其他表示失败.
int qspi_single_cmd( dw_qspi_regs_t * pqspi, uint8_t cmd )
{
	uint32_t temp;

	pqspi->SSIENR = 0;
	pqspi->SER = 0;

	// TMOD=0, DFS=7
	pqspi->CTRLR0 = 0x307;
	pqspi->TXFTLR = 0;

	//
	pqspi->SSIENR = 1;
	pqspi->DR[0] = cmd;
	pqspi->SER = 1;

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	pqspi->SER = 0;
	pqspi->SSIENR = 0;
	return 0;
}


/*
0x9F.
*/
int qspi_read_ident( dw_qspi_regs_t * pqspi, uint32_t * pidt )
{
	uint32_t temp;

	/* TMOD = 0 */
	pqspi->CTRLR0 = 0x307;
	pqspi->TXFTLR = 0;
	pqspi->CTRLR1 = 0;
	pqspi->SSIENR = 1;

	// cmd = 0x9F
	pqspi->DR[0] = 0x9F;
	pqspi->DR[1] = 0x55;
	pqspi->DR[2] = 0x55;
	pqspi->DR[3] = 0x55;
	pqspi->SER = 1;

	// wait : RXNE=1, TFE=1, BUSY=0
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x0D ) == 0xC )  {
			break;
		}
	}

	temp = pqspi->DR[0];
	temp = (temp << 8) | pqspi->DR[1];
	temp = (temp << 8) | pqspi->DR[2];
	temp = (temp << 8) | pqspi->DR[3];

	pqspi->SER = 0;	
	pqspi->SSIENR = 0;

	/* only tail 24bits */
	*pidt = temp & 0xFFFFFF;
	return 0;
}


/*
read fast : 1-1-1
cmd, 24-addr, 8-wait : data, data..
*/
int qspi_read_fast( dw_qspi_regs_t * pqspi, uint8_t cmd, uint32_t offs, int tlen, uint8_t * pdat )
{
uint32_t temp;

/* WAIT_CYCLES=8, TMOD=2 */
pqspi->SPI_CTRLR0 = 0x8104218;
pqspi->CTRLR0 = 0xB07;
pqspi->TXFTLR = 0;
pqspi->CTRLR1 = (uint32_t)(tlen - 1);
pqspi->SSIENR = 1;

// cmd = 0x03
pqspi->DR[0] = cmd;
pqspi->DR[1] = offs;
pqspi->SER = 1;

// 
for ( int i=0; i<tlen; i++ )  {

	while ( true )  {
		temp = pqspi->RXFLR;
		if ( temp > 0 )  {
			break;
		}
	}

	pdat[i] = (uint8_t)( pqspi->DR[0] );
}

// check busy..
while ( true )  {
	temp = pqspi->SR;
	if ( (temp & 0x5) == 0x4 )  {
		break;
	}
}

//
pqspi->SER = 0;	
pqspi->SSIENR = 0;	
return 0;
}

/*
read fast : 1-1-1
cmd, 24-addr, 8-wait : data, data..
*/
/// @brief 从nor-flash读取数据.QUAD模式.需要先设置QE.
/// @param pqspi 指向qspi寄存器的指针.
/// @param cmd 要发送的命令.
/// @param offs 要读取的地址偏移.
/// @param tlen 要读取的字节数.
/// @param pdat 存放读取数据的缓冲区.
/// @return 0表示成功, 其他表示失败.
int qspi_read_4_line( dw_qspi_regs_t * pqspi, uint8_t cmd, uint32_t offs, int tlen, uint8_t * pdat )
{
	uint32_t temp;

	/* TMOD=2 */
	//0xEB-4 WAIT_CYCLES-0x8182221(32bit addrs)
	//0x6B-8 WAIT_CYCLES-0x8184219      
	pqspi->SPI_CTRLR0 = 0x8182221;
	pqspi->CTRLR0 = 0x800807;
	pqspi->TXFTLR = 0;
	pqspi->CTRLR1 = (uint32_t)(tlen - 1);
	pqspi->SSIENR = 1;

	// cmd = 0xEB
	pqspi->DR[0] = cmd;
	pqspi->DR[1] = 0xaa;
	pqspi->SER = 1;

	// 
	for ( int i=0; i<tlen; i++ )  {

		while ( true )  {
			temp = pqspi->RXFLR;
			if ( temp > 0 )  {
				break;
			}
		}

		pdat[i] = (uint8_t)( pqspi->DR[0] );
	}

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;	
	return 0;
}

//flash qe enable
int qspi_qe_enable(dw_qspi_regs_t * pqspi){

	uint32_t temp;

	qspi_single_cmd(pqspi,0x06);//0x06-写启用
	
	//修改状态寄存器2 启动QE
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 10;

	// TMOD=0, DFS=7
	pqspi->CTRLR0 = 0x307;
	pqspi->TXFTLR = 0;

	/*GD25Q16E
		pqspi->DR[0] = 0x01;
		pqspi->DR[1] = 0x02; 
		pqspi->DR[2] = 0x02;  QE 使能
	*/ 
	/*W25Q128JVSIQ
		pqspi->DR[0] = 0x31;
		pqspi->DR[1] = 0x02;  QE 使能
	*/
	pqspi->SSIENR = 1;
	pqspi->SER = 1;
	pqspi->DR[0] = 0x01;
	pqspi->DR[1] = 0x02; 
	pqspi->DR[2] = 0x02;

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	pqspi->SER = 0;
	pqspi->SSIENR = 0;

	
	return 0;

}

int qspi_qe(){
	uint8_t statu;
	dw_qspi_regs_t *pqspi = (dw_qspi_regs_t *)0x2000000;


	qspi_xip_0();//xip_en = 0;


	qspi_read_status(pqspi,&statu,0x35);//0x35-状态寄存器2
	printf("default :status = 0x%02x\n",statu);

	qspi_qe_enable(pqspi);

	qspi_read_status(pqspi,&statu,0x35);//0x35-状态寄存器2
	printf("status = 0x%02x\n",statu);


	return 0;


}

int  qspi_Quad(){
	
	dw_qspi_regs_t *pqspi = (dw_qspi_regs_t *)0x2000000;
	uint8_t tary[64];

	qspi_read_4_line(pqspi,0xEB,0,64,tary);
	debug_dump_hex(tary,64);
	return 0;
}


/*
Fast Read Quad Output (6Bh)
*/
int qspi_read_quad( dw_qspi_regs_t * pqspi, uint32_t offs, int tlen, uint8_t * pdat )
{
	uint32_t * pu32 = (uint32_t *)pdat;
	uint32_t temp;

	// WAIT_CYCLES = 8, INST_L=8, ADDR_L=24,
	pqspi->SPI_CTRLR0 = 0x8104218;
	pqspi->CTRLR0 = 0xB1F;
	pqspi->TXFTLR = 0;
	pqspi->CTRLR1 = (uint32_t)(tlen >> 2);
	pqspi->SSIENR = 1;

	// cmd = 0x6B
	pqspi->DR[0] = 0x6B;
	pqspi->DR[1] = offs;

	for ( int i=0; i<(tlen>>2); i++ )  {

		while ( true )  {
			temp = pqspi->RXFLR;
			if ( temp > 0 )  {
				break;
			}
		}

		pu32[i] = pqspi->DR[0];
	}

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;	
	return 0;
}


/*
@brief
	根据命令读取对应status状态寄存器的值
	返回 status-0 , 用于检查 bit0 状态位, BUSY(WIP).
*/
int qspi_read_status( dw_qspi_regs_t * pqspi, uint8_t * pstus ,uint8_t cmd)
{
	uint32_t temp;

	// TMOD=3 (EEPROM Read mode), DFS=7
	pqspi->CTRLR0 = 0xF07;
	pqspi->TXFTLR = 0;

	// read 1 bytes.
	pqspi->CTRLR1 = 0;
	pqspi->SSIENR = 1;

	// cmd = 0x35
	pqspi->DR[0] = cmd;
	pqspi->SER = 1;

	// RXNE=1, TFE=1, BUSY=0
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x0D ) == 0xC )  {
			break;
		}
	}

	/* 返回读取内容.. */
	*pstus = (uint8_t)( pqspi->DR[0] );

	pqspi->SER = 0;	
	pqspi->SSIENR = 0;
	return 0;
	}


/*
@brief
姑且认为所有 nor-flash 型号的 sector 单位都是 4k bytes.
@param
addr, 使用字节地址, 虽然末尾 12 bits 被忽略.
*/
int qspi_erase_sector( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	uint8_t status;
	uint32_t temp;
	uint64_t tmout;

	/* WEL = 1 */
	qspi_single_cmd( pqspi, 0x06 );//0x06-写启用

	// TMOD=1 (Write only), DFS=7
	pqspi->CTRLR0 = 0x707;
	pqspi->TXFTLR = 0x0;

	// cmd=0x20
	pqspi->SSIENR = 1;
	pqspi->DR[0] = 0x20;
	pqspi->DR[1] = addr;
	pqspi->SER = 1;

	// temp_delay( 2000 );

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;

	// check status..
	tmout = time_make_timeout( 5000000 );
	while ( true )  {

		qspi_read_status( pqspi, &status ,0x5);

		/**/
		if ( (status & 0x1) == 0 )  {
			break;
		}

		if ( time_reached( tmout) )  {
			return 302;
		}
	}

	return 0;
}



/*
@brief
	这是一个简单的 page 写入操作, 没有考虑部分写入的场景.
*/
int qspi_program_page( dw_qspi_regs_t * pqspi, uint32_t addr, uint8_t * pdat )
{
	uint8_t status;
	uint32_t temp;
	uint64_t tmout;

	/* WEL = 1 */
	qspi_single_cmd( pqspi, 0x06 );
	
	// TMOD=1 (Write only), DFS=7
	pqspi->CTRLR0 = 0x707;
	pqspi->TXFTLR = 0;

	// cmd = 0x02
	pqspi->SSIENR = 1;
	pqspi->DR[0] = 0x02;
	pqspi->DR[1] = addr;
	pqspi->SER = 1;

	// write 
	for ( int i=0; i<32; i++ )  {
		
		while ( true )  {
			temp = pqspi->TXFLR;
			if ( temp <= 4 )  {
				break;
			}
		}

		pqspi->DR[0] = pdat[ (i*8) + 0 ];
		pqspi->DR[1] = pdat[ (i*8) + 1 ];
		pqspi->DR[2] = pdat[ (i*8) + 2 ];
		pqspi->DR[3] = pdat[ (i*8) + 3 ];

		pqspi->DR[4] = pdat[ (i*8) + 4 ];
		pqspi->DR[5] = pdat[ (i*8) + 5 ];
		pqspi->DR[6] = pdat[ (i*8) + 6 ];
		pqspi->DR[7] = pdat[ (i*8) + 7 ];
	}

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;


	// check flash status-0..
	tmout = time_make_timeout( 5000000 );
	while ( true )  {

		qspi_read_status( pqspi, &status,0x5 );

		/**/
		if ( (status & 0x1) == 0 )  {
			break;
		}

		if ( time_reached( tmout) )  {
			return 201;
		}
	}

	return 0;
}




int qspi_read( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	uint32_t temp;
	uint8_t tary[256];

	// disable
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 6;

	// TMOD=3 (EEPROM Read mode), DFS=7
	pqspi->CTRLR0 = 0xF07;
	pqspi->TXFTLR = 0x30000;

	// read 256 bytes.
	pqspi->CTRLR1 = 255;
	pqspi->SSIENR = 1;

	// cmd=0x3
	pqspi->DR[0] = 0x3;
	pqspi->DR[1] = (addr >> 16) & 0xFF;
	pqspi->DR[2] = (addr >> 8) & 0xFF;
	pqspi->DR[3] = addr & 0xFF;
	pqspi->SER = 1;

	// 
	for ( int i=0; i<256; i++ )  {

		while ( true )  {
			temp = pqspi->RXFLR;
			if ( temp > 0 )  {
				break;
			}
		}

		tary[i] = (uint8_t)( pqspi->DR[0] );
	}

	// dump...
	debug_dump_hex( tary, 256 );
	return 0;
}


int qspi_erase( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	uint32_t temp;
	//
	qspi_single_cmd( pqspi, 0x6 );

	//
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 10;

	// TMOD=1 (Write only), DFS=7
	pqspi->CTRLR0 = 0x707;
	pqspi->TXFTLR = 0x0;

	// cmd=0x20
	pqspi->SSIENR = 1;
	pqspi->DR[0] = 0x20;
	pqspi->DR[1] = addr;

#if  0
	pqspi->DR[1] = (addr >> 16) & 0xFF;
	pqspi->DR[2] = (addr >> 8) & 0xFF;
	pqspi->DR[3] = addr & 0xFF;
#endif

	pqspi->SER = 1;

	temp_delay( 2000 );

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;
	return 0;
}


int qspi_program( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	uint32_t temp;

	// disable
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 10;

	//
	qspi_single_cmd( pqspi, 0x6 );
	
	//
	pqspi->SSIENR = 0;
	pqspi->SER = 0;

	// TMOD=1 (Write only), DFS=7
	pqspi->CTRLR0 = 0x707;
	pqspi->TXFTLR = 0;

	// cmd=0x2
	pqspi->SSIENR = 1;
	pqspi->DR[0] = 0x02;
	pqspi->DR[1] = addr;

#if  0
	pqspi->DR[1] = (addr >> 16) & 0xFF;
	pqspi->DR[2] = (addr >> 8) & 0xFF;
	pqspi->DR[3] = addr & 0xFF;
#endif

	pqspi->SER = 1;

	// write incr data
	for ( int i=0; i<32; i++ )  {
		
		while ( true )  {
			temp = pqspi->TXFLR;
			if ( temp <= 4 )  {
				break;
			}
		}

		pqspi->DR[0] = (uint32_t)(i*8 + 0);
		pqspi->DR[1] = (uint32_t)(i*8 + 1);
		pqspi->DR[2] = (uint32_t)(i*8 + 2);
		pqspi->DR[3] = (uint32_t)(i*8 + 3);

		pqspi->DR[4] = (uint32_t)(i*8 + 4);
		pqspi->DR[5] = (uint32_t)(i*8 + 5);
		pqspi->DR[6] = (uint32_t)(i*8 + 6);
		pqspi->DR[7] = (uint32_t)(i*8 + 7);
	}

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x5) == 0x4 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;
	return 0;
}


int qspi_jedecid( dw_qspi_regs_t * pqspi )
{
	uint32_t temp;
	uint8_t tary[16];

	// disable
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 6;

	// TMOD=3 (EEPROM Read mode), DFS=7
	pqspi->CTRLR0 = 0xF07;
	pqspi->TXFTLR = 0;

	// read 256 bytes.
	pqspi->CTRLR1 = 15;
	pqspi->SSIENR = 1;

	// cmd=0x9F
	pqspi->DR[0] = 0x9F;
	pqspi->SER = 1;

	// 
	for ( int i=0; i<16; i++ )  {

		while ( true )  {
			temp = pqspi->RXFLR;
			if ( temp > 0 )  {
				break;
			}
		}

		tary[i] = (uint8_t)( pqspi->DR[0] );
	}

	// dump...
	debug_dump_hex( tary, 16 );
	return 0;
}


int qspi_status( dw_qspi_regs_t * pqspi, uint8_t cmd )
{
	uint32_t temp;
	uint8_t tary[16];

	// disable
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 6;

	// TMOD=3 (EEPROM Read mode), DFS=7
	pqspi->CTRLR0 = 0xF07;
	pqspi->TXFTLR = 0;

	// read 16 bytes.
	pqspi->CTRLR1 = 15;
	pqspi->SSIENR = 1;

	// cmd=0x9F
	pqspi->DR[0] = cmd;
	pqspi->SER = 1;

	// 
	for ( int i=0; i<16; i++ )  {

		while ( true )  {
			temp = pqspi->RXFLR;
			if ( temp > 0 )  {
				break;
			}
		}

		tary[i] = (uint8_t)( pqspi->DR[0] );
	}

	// dump...
	debug_dump_hex( tary, 16 );
	return 0;
}


/*
nor flash xip :

CTRLR0, CTRLR1, 
BAUDR, IMR, SER, 
SPI_CTRLR0(Dual/Quad/Octal), 
XIP_WRAP_INST, XIP_INCR_INST, XIP_CNT_TIME_OUT

a) 配置 standard spi 模式, 擦除和编程两种功能.
b) 配置 quad xip 模式, 能够读出来内容?
c) 配置 quad xip 模式, 测试没有指令的模式.
d) 配置 quad xip 模式, 测试 cache 使能之后的 wrap 读操作.

*/

int qspi_test( void * parg, int argc, const char * argv[] )
{
	int iret;
	uint32_t temp;
	dw_qspi_regs_t * pqspi = (dw_qspi_regs_t *)0x2000000;


	if ( argc < 2 )  {
		goto usage;
	}
	
	if ( 0 == strcmp( argv[1], "jid") )  {
		iret = qspi_jedecid( pqspi );
		return iret;
	}

	if ( argc < 3 )  {
		goto usage;
	}

	if ( 0 == strcmp( argv[1], "xip" ) )  {
		
		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse value fail = %d\n", iret );
			goto usage;
		}

		if ( temp == 0 )  {

			REG32( 0x200000 + 0x10 ) = 0x4;

		} else {

			REG32( 0x200000 + 0x10 ) = 0x5;	 

		}

	} else if ( 0 == strcmp( argv[1], "erase" ) ) {

		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse addr fail = %d\n", iret );
			goto usage;
		}

		//
		iret = qspi_erase( pqspi, temp );
		

	} else if ( 0 == strcmp( argv[1], "write" ) ) {

		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse addr fail = %d\n", iret );
			goto usage;
		}

		//
		iret = qspi_program( pqspi, temp );

	} else if ( 0 == strcmp( argv[1], "read" ) ) {
		
		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse addr fail = %d\n", iret );
			goto usage;
		}

		iret = qspi_read( pqspi, temp );

	} else if ( 0 == strcmp( argv[1], "status") ) {

		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse cmd fail = %d\n", iret );
			goto usage;
		}

		iret = qspi_status( pqspi, (uint8_t)temp );

	} else if ( 0 == strcmp( argv[1], "single") ) {

		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse cmd fail = %d\n", iret );
			goto usage;
		}

		iret = qspi_single_cmd( pqspi, (uint8_t)temp );
		iret += qspi_status( pqspi, 0x5 );

	} else  {
		goto usage;
	}
	
	//
	printf( "action ret = %d\n", iret );
	return 0;
	
usage:
	printf( "usage:\n" );
	printf( "\t%s xip <0/1>\n", argv[0] );
	printf( "\t%s erase <addr>\n", argv[0] );
	printf( "\t%s write <addr>\n", argv[0] );
	printf( "\t%s read <addr>\n", argv[0] );
	printf( "\t%s status <cmd>\n", argv[0] );
	printf( "\t%s single <cmd>\n", argv[0] );
	printf( "\t%s jid \n", argv[0] );
	return 0;
}



/// @brief  初始化 qspi 控制器.
/// @param  pqspi : qspi 控制器寄存器地址.
int qspi_init( dw_qspi_regs_t * pqspi )
{
	qc_sysctl_regs_t * psctl = (qc_sysctl_regs_t *)0x200000;

	/* xip = 0 */
	psctl->QspiCtrl = 4;

	/* qspi disable, div=6 */
	pqspi->SSIENR = 0;
	pqspi->SER = 0;

	pqspi->BAUDR = 6;
	pqspi->CTRLR0 = 0x307;
	pqspi->SPI_CTRLR0 = 0x8100218;

	return 0;
}

/*
flash target addr = 0.
*/
//将位于ILM中的数据烧录到QSPI Flash中
int qspi_burn( dw_qspi_regs_t * pqspi, uint8_t * pdat, int tlen )
{
	int iret;
	uint32_t * pu32;
	uint32_t temp;
	uint32_t offs;
	uint32_t endr;
	uint32_t tary[16];

	/**/
	qspi_init( pqspi );

	/* send 0xFF to flash, exit Quad Read */
	qspi_single_cmd( pqspi, 0xFF );

	/* 读取 SFDP 参数表, 确认是否在 qspi 连接正常? */
	qspi_read_fast( pqspi, 0x5A, 0, 64, (uint8_t *)tary );//0x5A-读取SFDP参数表
	if ( tary[0] != 0x50444653 )  {
		printf( "id=%lX\n", tary[0] );
		return 101;
	}
	
	/* todo : 检查 flash 里面的 status-1 寄存器,  修改 QE=1. */

	/* 补充 magic & tlen 信息.*/
	pu32 = (uint32_t *)pdat;
	pu32[12] = 0x4E5A4C58;		// (XLZN)
	pu32[13] = (uint32_t)tlen;
	pu32[14] = 0x01;		// load_way

	/* up align to 256. */
	endr = (uint32_t)tlen;
	temp = endr & 0xFF;
	if ( temp != 0 )  {
		endr = endr - temp + 256;
	}

	/* WAIT_CYCLES=0 */
	pqspi->SPI_CTRLR0 = 0x8100218;
	

	for ( offs=0; offs < endr; offs += 256 )  {

		if ( (offs & 0xFFF) == 0 )  {
			/* sector 4K bytes, erase */

			iret = qspi_erase_sector( pqspi, offs );
			if ( iret != 0 )  {
				return iret;
			}
		}

		iret = qspi_program_page( pqspi, offs, pdat + offs );

		if ( iret != 0 )  {
			return iret;
		}

	}
	
	/**/
	return 0;
}



/*
interrupt vector table :
entry-12 [0x30-0x33] : magic, 0x4E5A4C58
entry-13 [0x34-0x37] : tatal length.
entry-14 [0x38-0x3B] : check sum, crc32??
entry-15 [0x3C-0x3F] : 
*/
int qspi_load_flash( void * padr, int tmax )
{
	dw_qspi_regs_t * pqspi = (dw_qspi_regs_t *)0x2000000;
	uint32_t tary[16];
	int tlen;
	int offs;

	/**/
	qspi_init( pqspi );

	/* send 0xFF to flash, exit Quad Read */
	qspi_single_cmd( pqspi, 0xFF );



	/* 读取 SFDP 参数表, 确认是否在 qspi 连接正常? */
	qspi_read_fast( pqspi, 0x5A, 0, 64, (uint8_t *)tary );


	if ( tary[0] != 0x50444653 )  {   
		printf( "id=%lX\n", tary[0] );
		return 101;
	}
	
	/* check header magic : 0x4E5A4C58 (XLZN) */
	qspi_read_fast( pqspi, 0x0B, 0, 64, (uint8_t *)tary );
	if ( tary[12] != 0x4E5A4C58 )  {  
		printf( "magic=%lX\n", tary[0] );
		return 102;
	}

	/* 长度不能超过 目的内存空间. */
	if ( (tary[13] < 256) || (tary[13] > tmax) )  {
		return 102;
	}

	/* get length, read all.. */
	tlen = (int)tary[13];
	offs = 0;

	if ( tlen > 0x10000 )  {
		tlen = tlen - 0x10000;
		qspi_read_fast( pqspi, 0x0B, 0, 0x10000, padr );
		offs = offs + 0x10000;
	}
	
	padr = (void *)( (uintptr_t)padr + offs );
	qspi_read_fast( pqspi, 0x0B, offs, tlen, padr );
	return 0;
}


int flash_execute(void){

	dw_qspi_regs_t * pqspi = (dw_qspi_regs_t *)0x2000000;
	uint32_t tary[16];
	uint8_t data[256];
	uintptr_t pjbuf[4];


	pjbuf[0] = (uintptr_t)ONCHIP_DLM_BASE;
	pjbuf[1] = (uintptr_t)0x2000000;
	pjbuf[2] = (uintptr_t)(ONCHIP_ILM_BASE);
	pjbuf[3] = 0;

	qspi_xip_0();//xip_en = 0;


	qspi_single_cmd( pqspi, 0xFF );
	//判断是否外接有flash
	/* 读取 SFDP 参数表, 确认是否在 qspi 连接正常? */
	qspi_read_fast( pqspi, 0x5A, 0, 64, (uint8_t *)tary );//0x5A-读取SFDP参数表
	if ( tary[0] == 0x50444653 )  {
		printf( "flash found\n" );
	}else  {
		printf( "no flash found\n" );
		return -1;
	}

	//四线读 需使能QE位
	qspi_qe_enable(pqspi); //设置QE位

	//延时 --在QE位 使能与四线读取之间需要延时--QE位使能后 需要缓冲时间 才能继续读数？
	for ( int i=0; i<1000000; i++ )  {
		__NOP();
	}	

	//四线读取数据-EB
	qspi_read_4_line(pqspi,0xEB,0,64,data);
	//判断magic-0x584C5A4E-xlzn
	if(data[48] == 0x58 && data[49] == 0x4C && data[50] == 0x5A && data[51] == 0x4E){
		printf("supported four-line\n");
		//jump to flash execute
		load_xip_four_line();
		
		//延时--wait flash transfer complete
		for ( int i=0; i<1000000; i++ )  {
			__NOP();
		}

		__builtin_longjmp( pjbuf, 1 );//跳转到pjbuf[1]地址
		return 0;
	}
	
	printf("not supported four-line\n");
	return -2;
}


int qspi_cmd_fast( void * parg, int argc, const char * argv[] )
{
	int iret;
	uint32_t temp;
	uint8_t tary[128];
	dw_qspi_regs_t * pqspi = (dw_qspi_regs_t *)0x2000000;


	if ( argc < 2 )  {
		printf( "fast read :  cmd-code need...\n" );
		return 1;
	}

	iret = debug_str2uint( argv[1], &temp );
	if ( iret != 0 )  {
		printf( "code parse fail %d\n", iret );
		return 2;
	}

	temp = temp & 0xFF;
	qspi_read_fast( pqspi, (uint8_t)temp, 0, 64, tary );

	/**/
	debug_dump_hex( tary, 64 );
	return 0;
}


int qspi_cmd_quad( void * parg, int argc, const char * argv[] )
{
	return 0;
}


//flash_xip_way eb command--no instruction phase
int load_xip_four_line(){
	dw_qspi_regs_t *pqspi = (dw_qspi_regs_t *)0x2000000;

	printf("flash_xip_way eb command--no instruction phase\n");

		REG32( 0x200000 + 0x10 ) = 0x4;


		/* 发送0xFF to flash  退出四线*/
		qspi_single_cmd( pqspi, 0xFF );

		//发送指令给flash(进入到0xEB模式，传输无需指令阶段模式)
		pqspi->SSIENR = 0;
		pqspi->SPI_CTRLR0 = 0x182221;
		pqspi->CTRLR0 = 0x800807;
		pqspi->TXFTLR = 0;
		pqspi->CTRLR1 = 1;
		pqspi->SSIENR = 1;
		// cmd = 0xEB
		pqspi->DR[0] = 0xEB;
		pqspi->DR[1] = 0x000000aa;
		pqspi->SER = 1;

		//延时
		for ( int i=0; i<1000; i++ )  {
			__NOP();
		}


		//xip 四线配置(EB命令-0xaa模式 无指令阶段)：
		pqspi->SSIENR = 0;
		pqspi->CTRLR0 = 0x800807;//Qual SPI Format,TX_ONLY,07-bit serial data transfer
		//EBh 需要enable SPI_CTRLR0.XIP_MD_BIT_EN(7位) 
		//disable SPI_CTRLR0.XIP_INST_EN(20位)  SPI_CTRLR0.INST_L(9:8 INST_L) = 0
		pqspi->SPI_CTRLR0 = 0x8082099;
		pqspi->XIP_MODE_BITS = 0xaa;
		pqspi->XIP_INCR_INST = 0xEB;//Quad I/O Fast Read (EBH) command
		pqspi->SSIENR = 1;

		REG32( 0x200000 + 0x10 ) = 0x5;

		return 0;
}

